import org.openkinect.*;
import org.openkinect.processing.*;
import ddf.minim.*;
import ddf.minim.analysis.*;

final int SKIP = 4;
final int DENCITY = 16;
final int SENSITIVITY = 10;
final float LIMIT = 1.5f;
final float SCASTR = 32.0f;

// Kinect variables
Kinect kinect;
float[] depthLookUp = new float[2048];

// Graphics variables
PImage img;             // Captured color object
float bgHue = 0.0f;
float bgBri = 16.0f;
float angle = 0.0f;

// Sound variables
Minim minim;
AudioPlayer music;
BeatDetect bd;
BeatListener bl;
FFT fft;

// Particle variable
ArrayList particles;          // Array of particles

void setup()
{
  /// Init graphics
  size(320,240,P3D);
  frameRate(60);
  colorMode(HSB);
  //smooth();

  /// Init Kinect
  kinect = new Kinect(this);
  kinect.start();
  kinect.enableRGB(true);
  kinect.enableDepth(true);
  kinect.processDepthImage(false);
  
  // Make the lookup table
  for (int i = 0; i < depthLookUp.length; i++) {
    depthLookUp[i] = rawDepthToMeters(i);
  }
  img = kinect.getVideoImage();

  /// Init sound
  minim = new Minim(this);
  music = minim.loadFile("music.mp3");
  bd = new BeatDetect(music.bufferSize(), music.sampleRate());
  bd.setSensitivity(SENSITIVITY);  
  bl = new BeatListener(bd, music);
  fft = new FFT(music.bufferSize(), music.sampleRate());

  /// Init particles
  particles = new ArrayList();
}

void draw()
{
  // Set display
  background(bgHue%255,255,bgBri);
  
  // Get raw depth
  int[] depth = kinect.getRawDepth();

  /// Transform coordinate  
  translate(width/2,height/2,-2000);
  rotateX(cos(angle)*0.1f);
  rotateY(tan(angle)*0.05f);
  rotateY(sin(angle)*0.3f);

  /// Check sound trigger
  if (music.isPlaying() && bd.isKick())
  {    
    bgHue += 1;
    bgBri = 32;
    translate(random(-50,50),random(-50,50),random(-50,50));
    angle = random(0,2*PI);
  }
  if (music.isPlaying() && bd.isSnare())
  {
  }
  if (music.isPlaying() && bd.isHat())
  {
  }

  fft.forward(music.mix);
  
  // Image rendering loop  
  for (int y = 0; y < height; y += SKIP) 
  {
    for (int x = 0; x < width; x += SKIP)
    {
      int i = x+y*width;
      int rawDepth = depth[i];   
      PVector v = depthToWorld(x,y,rawDepth);
      
      if ( v.z < LIMIT ) //&& (int)random(0,DENCITY)%DENCITY == 0)
      {        
        // Set grapics variables
        color col = img.pixels[i];
        int h = (int)hue(col);
        int s = (int)saturation(col);
        float b = brightness(col);
        float strengthB = sqrt(fft.getBand(h*2)); // [0,8]    
        float factor = 500.0f;
        float siz = 1.0f * v.z;//random(1,8);
        float coeff = map(strengthB,0.0f,8.0f,1.0f,SCASTR);  // sqrt(64)
        float sizZ = siz * coeff;

        pushMatrix();
        
          rotateX(PI*(-(float)y/height+0.5f));
          rotateY(2*PI*(-(float)x/width+0.5f));      
          translate(0, 0, v.z*factor);
          //translate(0,0,sizZ/2.0f);
  
          //b = constrain(strengthB*32.0f, 64.0f, 255.0f);
          col = color(h,s,b);
          noFill();
          stroke(col);
          line(0,0,v.z*factor, 0,0,v.z*factor+sizZ*6);
          //box(siz,siz,max(sizZ,siz));
    
        popMatrix();

        //particles.add(new Particle(v, scaZ, siz, col));
      }
    }
  }

  // Loop rendering particles
  for (int i=0; i<particles.size(); i++)
  {
    Particle p = (Particle)particles.get(i);
    if (p.life < 0.0)
    {
      particles.remove(i);
      i--;
    }
    p.render();
    p.update(); 
  }

  // postprocess
  angle += 0.03f;
  if (angle > 2*PI) angle = 0;
  bgHue += 0.1f;
  bgBri -= 1.0f;
  if (bgBri<16.0f) bgBri = 16.0f;
}

void mousePressed()
{
  // Play music
  if (music.isPlaying()==false) music.loop();
}

float rawDepthToMeters(int depthValue)
{
  if (depthValue < 2047) {
    return (float)(1.0 / ((double)(depthValue) * -0.0030711016 + 3.3309495161));
  }
  return 0.0f;
}

PVector depthToWorld(int x, int y, int depthValue)
{
  final double fx_d = 1.0 / 5.9421434211923247e+02;
  final double fy_d = 1.0 / 5.9104053696870778e+02;
  final double cx_d = 3.3930780975300314e+02;
  final double cy_d = 2.4273913761751615e+02;

  PVector result = new PVector();
  double depth =  depthLookUp[depthValue];//rawDepthToMeters(depthValue);
  result.x = (float)((x - cx_d) * depth * fx_d);
  result.y = (float)((y - cy_d) * depth * fy_d);
  result.z = (float)(depth);
  return result;
}

public void stop()
{
  kinect.quit();
  
  music.close();
  minim.stop();
  
  super.stop();
}

